1 00:00:00,580 --> 00:00:01,300 Hey there. 2 00:00:01,300 --> 00:00:02,350 Welcome back. 3 00:00:02,350 --> 00:00:09,310 In this lecture we're going to be exploring the task library which gives us very useful sets of functions 4 00:00:09,310 --> 00:00:12,100 for scheduling things to happen in our code. 5 00:00:12,100 --> 00:00:18,580 It's called the task library because it works directly with the Roblox Task Scheduler, which basically 6 00:00:18,580 --> 00:00:22,120 sets what order code should be executed in the game engine. 7 00:00:22,120 --> 00:00:28,810 Now, you've already seen us use the task dot wait function, and this function goes and yields the 8 00:00:28,810 --> 00:00:33,100 current thread of execution for a set amount of time that we pass here. 9 00:00:33,100 --> 00:00:39,280 And the task wait function also returns a number, and this number represents the amount of time that 10 00:00:39,280 --> 00:00:41,710 our task wait function actually yielded for. 11 00:00:41,710 --> 00:00:46,180 So we'll call this yielded time, and we'll wait for one second. 12 00:00:46,180 --> 00:00:47,260 And then we can print. 13 00:00:47,260 --> 00:00:49,570 This yielded time into the console. 14 00:00:49,570 --> 00:00:55,300 And let's go ahead and see actually how long our task dot wait for one second actually yielded for. 15 00:00:55,300 --> 00:01:00,520 So if we run the game here and look inside of our console, you can see that our task wait function 16 00:01:00,520 --> 00:01:06,160 actually yielded for about 1.01165 seconds. 17 00:01:06,160 --> 00:01:12,520 The reason why this value isn't perfect is because of how the task scheduler works. 18 00:01:12,520 --> 00:01:18,460 Yielded threads are going to be checked on a frame by frame basis, which is causing this small discrepancy 19 00:01:18,460 --> 00:01:20,230 in the timestamp of task dot. 20 00:01:20,230 --> 00:01:25,360 Wait, so it's never going to be perfectly waiting for one second, but it's going to be pretty stinkin 21 00:01:25,360 --> 00:01:26,080 close. 22 00:01:26,560 --> 00:01:31,690 Now let's go ahead and take a look at some of the other functions the task library has to offer. 23 00:01:31,690 --> 00:01:34,360 One of them is called task dot spawn. 24 00:01:34,360 --> 00:01:38,530 So let me go ahead and refer to my task library and get the spawn function. 25 00:01:38,530 --> 00:01:45,430 And it says here it says calls or resumes a function slash coroutine immediately through the engine 26 00:01:45,430 --> 00:01:46,090 scheduler. 27 00:01:46,090 --> 00:01:50,410 So as you can see, it's working directly with the task scheduler to make this happen. 28 00:01:50,410 --> 00:01:52,540 So we can pass a function here. 29 00:01:53,090 --> 00:01:59,540 And what's going to happen is it's going to execute or spawn this function immediately in a coroutine 30 00:01:59,540 --> 00:02:00,260 thread. 31 00:02:00,260 --> 00:02:06,020 And if you remember back in our lecture about coroutine threads, the coroutine thread data type isn't 32 00:02:06,020 --> 00:02:10,970 actually what we would think as a thread on a processor in a computer. 33 00:02:10,970 --> 00:02:16,100 And that's because Lua is a single threaded programming language, so everything is still going to be 34 00:02:16,100 --> 00:02:18,740 executed on one computer thread. 35 00:02:18,740 --> 00:02:25,190 But we do have this idea of coroutine threads that allow us to handle multiple different tasks at the 36 00:02:25,190 --> 00:02:26,180 same time. 37 00:02:26,180 --> 00:02:32,420 So inside of this task dot spawn function, I'm just going to have a for loop that's going to loop ten 38 00:02:32,420 --> 00:02:32,840 times. 39 00:02:32,840 --> 00:02:35,510 So we'll do I to 1 to 10. 40 00:02:36,100 --> 00:02:38,380 And we'll just print I out into the console. 41 00:02:38,380 --> 00:02:46,150 And then outside of this function, I'm going to print something like a statement and say this was not 42 00:02:46,150 --> 00:02:47,110 blocked. 43 00:02:47,230 --> 00:02:52,810 Now because Lua is single threaded, what's going to happen is the interpreter is going to go through 44 00:02:52,840 --> 00:02:52,990 here. 45 00:02:52,990 --> 00:02:56,110 It's going to hit this function call for task dot spawn. 46 00:02:56,110 --> 00:03:00,850 And it's going to be like, oh, okay, I got to go in here and execute this code immediately. 47 00:03:00,850 --> 00:03:07,150 So what task dot spawn does is it spawns a new coroutine thread and executes it right away immediately. 48 00:03:07,150 --> 00:03:11,530 So it's going to go through here and execute this print statement ten times. 49 00:03:11,530 --> 00:03:13,750 So it's going to print that ten times in the console. 50 00:03:13,750 --> 00:03:15,070 And then it's going to print. 51 00:03:15,070 --> 00:03:16,930 This was not blocked. 52 00:03:16,930 --> 00:03:20,350 So let me go ahead and comment this out and let's run our game. 53 00:03:21,110 --> 00:03:22,220 And inside of the console. 54 00:03:22,220 --> 00:03:25,190 As you can see, one, two, three all the way to ten. 55 00:03:25,190 --> 00:03:29,630 And then afterward our print statement of this was not blocked, got executed. 56 00:03:30,150 --> 00:03:36,030 Now if our tasked spawn function had a yield in here, for example, through every iteration of our 57 00:03:36,030 --> 00:03:40,530 for loop, let's say we yielded for a short period of time. 58 00:03:40,530 --> 00:03:46,050 Well, now what's going to happen is the main thread of execution is going to go through here. 59 00:03:46,050 --> 00:03:50,520 It's going to hit this for loop and print the first index which will be one. 60 00:03:50,520 --> 00:03:52,590 And it's going to hit this yield statement. 61 00:03:52,590 --> 00:03:58,380 And it's going to be like okay, now it's time for me to exit out of here and execute any other tasks 62 00:03:58,380 --> 00:04:00,450 that are currently waiting to be executed. 63 00:04:00,450 --> 00:04:03,660 And one example of that would be our print statement down here. 64 00:04:03,660 --> 00:04:08,610 So it's going to hop out of our for loop and come down and print this statement. 65 00:04:08,610 --> 00:04:14,190 And then once it has to come back in here, once this yield statement is up, then it'll print the second 66 00:04:14,190 --> 00:04:16,260 letter and then the third and so on and so forth. 67 00:04:16,260 --> 00:04:22,140 And every time it hits this function to yield the current thread, the current thread is going to hop 68 00:04:22,140 --> 00:04:27,270 out and execute any other important game stuff that's happening in our game. 69 00:04:27,870 --> 00:04:33,570 So if we run our game now, what you're going to see is instead of our print statement being printed 70 00:04:33,570 --> 00:04:39,720 at the end, it's going to be printed second, because first it printed one, then it hit that yield 71 00:04:39,720 --> 00:04:42,300 statement which caused it to go down and print. 72 00:04:42,300 --> 00:04:43,410 This was not blocked. 73 00:04:43,410 --> 00:04:48,120 And then it came back to our for loop and printed the rest of our numbers. 74 00:04:48,600 --> 00:04:53,610 Now there's another function in the task library called task dot defer. 75 00:04:53,610 --> 00:04:57,210 And this function is extremely similar to task dot spawn. 76 00:04:57,210 --> 00:04:59,190 But there is a slight difference. 77 00:04:59,190 --> 00:05:06,180 And actually Roblox recommends for you to use task dot defer over task dot spawn if needed. 78 00:05:06,180 --> 00:05:08,520 And what task dot defer does. 79 00:05:08,520 --> 00:05:10,470 We're going to call defer here. 80 00:05:10,470 --> 00:05:12,600 And actually let's go ahead and read what it says. 81 00:05:12,600 --> 00:05:18,960 It says calls slash resumes a function or coroutine on the next resumption cycle. 82 00:05:18,960 --> 00:05:20,040 What does that mean. 83 00:05:20,040 --> 00:05:26,010 Well you can basically think of the resumption cycle as all of the stuff that needs to be executed in 84 00:05:26,010 --> 00:05:27,120 the current frame. 85 00:05:27,120 --> 00:05:32,010 So the first thing that needs to be executed is like rendering all the stuff on the screen. 86 00:05:32,010 --> 00:05:37,110 The next thing that needs to be executed is handling all the physics, and then the next one would be 87 00:05:37,110 --> 00:05:43,980 other stuff, until eventually you'll get to a section in the frame at the end of a frame called deferred 88 00:05:43,980 --> 00:05:44,970 threads. 89 00:05:44,970 --> 00:05:51,270 And this section of deferred threads is going to be a queue of deferred tasks that need to be executed 90 00:05:51,270 --> 00:05:53,880 by the game engine and task. 91 00:05:53,880 --> 00:06:01,440 Dot defer allows us to add a coroutine thread into that queue of deferred tasks, and execute it at 92 00:06:01,440 --> 00:06:04,140 a later point in the current frame. 93 00:06:04,140 --> 00:06:06,480 So that's why it's called task dot defer. 94 00:06:06,480 --> 00:06:13,980 It's because we're deferring a task to be executed at a later point in the current thread. 95 00:06:14,790 --> 00:06:18,690 So let's go ahead and defer a new function here. 96 00:06:18,690 --> 00:06:21,300 And we're going to be doing the exact same thing that we did up here. 97 00:06:21,300 --> 00:06:22,800 We're just going to paste that there. 98 00:06:22,800 --> 00:06:25,080 But I'm not going to yield here. 99 00:06:25,080 --> 00:06:28,350 And then let me go ahead and comment this out. 100 00:06:28,350 --> 00:06:31,710 And I'm going to copy this and print this down here. 101 00:06:31,710 --> 00:06:38,310 Now what's going to happen is because this coroutine thread is going to be pushed at a later point in 102 00:06:38,310 --> 00:06:45,390 the current frame, this is going to execute first and then this is going to execute afterwards. 103 00:06:45,390 --> 00:06:47,430 So let's go ahead and run our game. 104 00:06:48,260 --> 00:06:53,630 And if you look as you can see, our print statement executed first, this was not blocked. 105 00:06:53,630 --> 00:06:58,070 And then afterwards our loop went through and executed all of the different numbers. 106 00:06:58,070 --> 00:07:05,000 And that's because that for loop that was inside of that thread got pushed into that queue of deferred 107 00:07:05,000 --> 00:07:06,080 tasks. 108 00:07:06,960 --> 00:07:13,860 So task De-spawn and task dot defer are going to both execute in the same frame. 109 00:07:13,860 --> 00:07:18,870 The only difference is at what point in the frame they are going to be executed. 110 00:07:18,900 --> 00:07:26,430 Task dot spawn is going to execute this thread right away immediately, while task dot defer is going 111 00:07:26,430 --> 00:07:32,700 to place it into that queue of deferred tasks at a later point in the current frame. 112 00:07:32,700 --> 00:07:35,310 That's the difference between those two functions. 113 00:07:35,310 --> 00:07:42,180 Now Roblox themselves recommend for you to use task Dot defer most of the time, unless you really need 114 00:07:42,180 --> 00:07:47,160 to spawn a task or a thread right away and have it execute immediately. 115 00:07:47,160 --> 00:07:50,610 For the most part, you should be using task dot defer. 116 00:07:51,060 --> 00:07:55,710 Now, the next function we're going to take a look at is called task dot delay. 117 00:07:55,860 --> 00:08:02,520 And this one's a pretty neat one because it allows us to pass a number and then a function or a thread 118 00:08:02,520 --> 00:08:06,870 and it says schedules a function slash coroutine to be called slash. 119 00:08:06,870 --> 00:08:12,810 Resumed on the next heartbeat after the given duration in seconds has passed without throttling. 120 00:08:12,810 --> 00:08:19,830 So basically we can pass a number here to define how long we want to wait before we execute this function. 121 00:08:19,830 --> 00:08:24,600 So for example, I can pass a value of five here and then a function. 122 00:08:24,600 --> 00:08:29,220 And this function will execute five seconds in the future. 123 00:08:29,640 --> 00:08:32,340 So let me go ahead and comment this out up here. 124 00:08:32,340 --> 00:08:35,460 And let's go ahead and copy this paste that down there. 125 00:08:35,460 --> 00:08:38,160 And then we can just do the same thing with the for loop. 126 00:08:38,160 --> 00:08:41,640 So I'll just paste that there and let me get rid of these comments. 127 00:08:42,520 --> 00:08:47,800 So now we're going to have a coroutine thread that is currently delayed for five seconds. 128 00:08:47,800 --> 00:08:51,130 So five seconds in the future is going to execute this for loop. 129 00:08:51,130 --> 00:08:55,210 So that means this print statement down here is going to execute first. 130 00:08:55,210 --> 00:08:56,950 So let's go ahead and run the game. 131 00:08:57,610 --> 00:09:01,270 There is our print statement and now we have to wait five seconds. 132 00:09:02,050 --> 00:09:03,370 And then there we go. 133 00:09:03,370 --> 00:09:06,400 Our for loop executed and printed our ten numbers. 134 00:09:06,400 --> 00:09:14,020 And if you take a look at the timestamp, it has waited roughly five seconds to then execute that thread 135 00:09:14,020 --> 00:09:15,190 in the future. 136 00:09:15,900 --> 00:09:21,990 Now you keep hearing me calling these coroutine threads, and that's because the task dot delay, the 137 00:09:21,990 --> 00:09:28,080 task dot defer, and the task dot spawn function all return a coroutine thread data type. 138 00:09:28,440 --> 00:09:34,260 So if we actually take a look at task dot delay and we look at the end, it says it returns a thread. 139 00:09:34,290 --> 00:09:37,170 The thread data type which is a coroutine thread. 140 00:09:37,260 --> 00:09:41,310 And that means we're able to store this thread in a variable. 141 00:09:41,400 --> 00:09:46,080 And because we now have this thread, we're able to check many different things about this thread, 142 00:09:46,080 --> 00:09:48,030 such as the status of the thread. 143 00:09:48,030 --> 00:09:52,980 And we're also able to cancel these threads with another function that we're going to take a look at 144 00:09:52,980 --> 00:09:53,880 in a little bit. 145 00:09:54,730 --> 00:10:00,250 But for example, what I'm going to do down here is I'm going to print and I'm going to use the coroutine 146 00:10:00,250 --> 00:10:07,450 library and use the status function to print the current status of this thread right here that is delayed. 147 00:10:07,450 --> 00:10:12,160 So we're going to call coroutine dot status and then pass our thread. 148 00:10:12,160 --> 00:10:14,980 And let's go ahead and see what it prints inside of the console. 149 00:10:16,420 --> 00:10:18,640 So as you can see, it prints out suspended. 150 00:10:18,640 --> 00:10:24,730 And that's because that coroutine thread is currently suspended and we're waiting for it to execute 151 00:10:24,730 --> 00:10:26,500 at some point in the future. 152 00:10:26,500 --> 00:10:31,270 And then after it goes through and executes through that for loop and it reaches the end of the function, 153 00:10:31,270 --> 00:10:34,600 then that thread is going to be placed in a dead state. 154 00:10:36,160 --> 00:10:42,250 Now, because each of these functions return a thread data type, we are able to cancel them using a 155 00:10:42,250 --> 00:10:46,390 function in the task library called task dot cancel. 156 00:10:47,270 --> 00:10:54,080 So if we take a look at the task cancel function it says cancels a thread preventing it from being resumed. 157 00:10:54,080 --> 00:10:56,840 And all we need to do is pass a thread here. 158 00:10:57,420 --> 00:11:03,960 So what I'm going to do is I'm also going to comment out this up here and let's go ahead and spawn a 159 00:11:03,960 --> 00:11:04,770 function. 160 00:11:06,130 --> 00:11:08,620 And we'll just do the same kind of for loops. 161 00:11:08,620 --> 00:11:12,940 So for I equal to 1 to 10 we'll print out I. 162 00:11:13,330 --> 00:11:15,670 And then I'll put a yield statement in here. 163 00:11:15,670 --> 00:11:17,620 And we'll yield for like one second. 164 00:11:18,940 --> 00:11:21,910 And then we can go ahead and store that thread in a variable. 165 00:11:23,120 --> 00:11:30,620 And now let's say I wanted to cancel this for loop after let's say three seconds of executing. 166 00:11:30,620 --> 00:11:33,290 So let's go ahead and yield for three seconds down here. 167 00:11:33,290 --> 00:11:37,400 And then afterwards we're going to cancel the current thread. 168 00:11:37,400 --> 00:11:41,960 And then I'm going to print a message called thread cancel. 169 00:11:41,960 --> 00:11:43,280 We'll say something like that. 170 00:11:43,960 --> 00:11:47,560 So now what should happen is this for loop should print. 171 00:11:47,560 --> 00:11:51,010 I believe it should print one, two, three, but it might print 1 to 2. 172 00:11:51,010 --> 00:11:54,100 And then afterwards we're going to cancel that thread. 173 00:11:54,100 --> 00:11:55,870 We'll print out thread cancelled. 174 00:11:55,870 --> 00:12:02,230 And now any of the other code that we were supposed to execute in here, like the additional iterations 175 00:12:02,230 --> 00:12:05,740 through the loop, we're no longer going to execute it because we've canceled the thread. 176 00:12:07,570 --> 00:12:14,950 So if we take a look, we got one, two, three and then it said thread canceled and now we're done. 177 00:12:15,340 --> 00:12:21,220 So just like that, if we wanted to prematurely cancel or stop a particular thread from executing, 178 00:12:21,220 --> 00:12:24,550 we can do so using the task dot cancel function. 179 00:12:24,550 --> 00:12:28,810 And we can also do the same thing with an infinitely executing thread. 180 00:12:28,810 --> 00:12:31,450 So like if we had a while loop in here. 181 00:12:32,770 --> 00:12:38,950 And this while loop was simply just printing out hello or something like that. 182 00:12:38,950 --> 00:12:42,370 And then we'll put a yield statement here. 183 00:12:42,580 --> 00:12:48,730 What we'll go ahead and do is we'll actually just wait for one second, and then after one second we'll 184 00:12:48,730 --> 00:12:51,490 cancel that thread and print out thread cancel. 185 00:12:51,520 --> 00:12:52,960 So if we run. 186 00:12:54,580 --> 00:12:55,180 There you go. 187 00:12:55,180 --> 00:13:00,430 We got 25 hello statements printed into our console, and then the thread got cancelled. 188 00:13:00,430 --> 00:13:04,960 And the while loop is no longer executing, which is pretty cool. 189 00:13:05,600 --> 00:13:11,930 Now, I would not recommend canceling infinite loops using task dot cancel. 190 00:13:11,930 --> 00:13:18,530 You should instead set it up in a way where your while loop takes a condition instead, and when that 191 00:13:18,530 --> 00:13:21,680 condition is set to false, then the loop will end. 192 00:13:21,710 --> 00:13:27,710 This just makes it a bit clearer to anyone who is reading your code exactly what is happening, instead 193 00:13:27,710 --> 00:13:31,340 of your infinite loop being cancelled somewhere else in your script. 194 00:13:31,340 --> 00:13:35,600 Because if I look at a while true do loop, I'm assuming. 195 00:13:35,600 --> 00:13:43,130 Hey, this is going to be executing forever throughout the entire duration of our game, but if we're 196 00:13:43,130 --> 00:13:49,190 cancelling that while loop somewhere else in our script, then my assumption was not correct. 197 00:13:49,190 --> 00:13:54,530 So instead, you should set these while loops up in a way where you'll have a condition instead, and 198 00:13:54,530 --> 00:13:59,270 then set that condition to false when you want your while loop to stop executing. 199 00:13:59,930 --> 00:14:03,950 There are two other functions in the task library. 200 00:14:04,310 --> 00:14:06,200 These functions are. 201 00:14:06,200 --> 00:14:07,250 Let me see if I can find them. 202 00:14:07,250 --> 00:14:11,420 They are tasks dot synchronize and task dot d synchronize. 203 00:14:11,420 --> 00:14:19,610 Do not worry about these functions because they relate to parallel Lua, which is Lua code executing 204 00:14:19,610 --> 00:14:21,980 on multiple processor threads. 205 00:14:21,980 --> 00:14:25,820 And that's too complex to be discussing about this early in the course. 206 00:14:25,820 --> 00:14:29,330 So again, do not worry about these two other functions. 207 00:14:30,260 --> 00:14:34,970 Okay, so let's go through a quick recap of everything we've just learned. 208 00:14:35,000 --> 00:14:41,690 We know the task wave function allows the current thread to yield, and it returns to us a number that 209 00:14:41,690 --> 00:14:46,430 defines the actual amount of time that the function yielded for the task. 210 00:14:46,610 --> 00:14:52,970 Spawn function allows us to spawn a new coroutine thread and execute it right away at the current frame, 211 00:14:53,480 --> 00:15:00,710 and the task dot defer function allows us to spawn a coroutine thread in the current frame, but instead 212 00:15:00,710 --> 00:15:07,370 we defer it and place it in a queue so it executes at a later point in the current frame. 213 00:15:07,610 --> 00:15:14,000 We also know about the task dot delay function, which allows us to delay a thread or a function from 214 00:15:14,000 --> 00:15:16,730 executing at some point in the future. 215 00:15:16,730 --> 00:15:23,780 If we were to set this value here to zero, then this function or thread is going to be executing at 216 00:15:23,780 --> 00:15:25,250 the next frame. 217 00:15:25,550 --> 00:15:32,510 Last but not least, we know about the task dot cancel function, which allows us to pass a thread which 218 00:15:32,510 --> 00:15:39,560 is returned from our task dot spawn or task dot delay or task dot defer functions, and we're able to 219 00:15:39,560 --> 00:15:42,590 cancel that thread and prevent it from executing. 220 00:15:43,580 --> 00:15:49,670 So other than that, the task library is an incredibly useful and important library that you'll be using 221 00:15:49,670 --> 00:15:51,080 a lot in your code. 222 00:15:51,080 --> 00:15:56,000 There are so many things you can do with the task library, because it helps us schedule our code, 223 00:15:56,000 --> 00:15:59,300 and we'll be using this library a lot in our projects. 224 00:15:59,300 --> 00:16:02,630 Thanks for watching and I'll see you in the next lecture.